package edu.zju.udms.dnavigator.core.server.servlets;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import edu.zju.udms.dnavigator.core.entity.ExtractorField;
import edu.zju.udms.dnavigator.core.entity.FieldTypeEnum;
import edu.zju.udms.dnavigator.core.extract.Extractor;
import net.sf.json.JSONObject;

@WebServlet(urlPatterns={"/api/source"})
public class ApiSourceServlet extends NavigatorServlet {

	/**
	 * 
	 */
	private static final long serialVersionUID = -5945614393530674564L;
	private static final Logger logger = Logger.getLogger(ApiSourceServlet.class);


	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
	        throws ServletException, IOException {
		logger.info("GET /api/source");
		JSONObject res = new JSONObject();

		List<Extractor> extractors = config.listExtractors();

		List<Object> infoList = new ArrayList<>();  // 通用信息
		List<Object> panelList = new ArrayList<>(); // 对应显示在面板上的数据
		int totalCount = 0;
		HashMap<String, Integer> sourceTypeCount = new HashMap<>();  // 统计每种source的数量
		HashMap<String, List<String>> sourceWords = new HashMap<>();  // 统计每种source添加或更新所需要的字段
		HashMap<String, String> sourceClass = new HashMap<>(); // 统计每种source对应的java类

		// 获取每个extractor的信息字段
		for (Extractor e: extractors) {
			totalCount += 1;
			Class<?> clazz = e.getClass();
			
			Field[] fields = clazz.getDeclaredFields();
			String source = e.getSource();

			try {
				boolean needsUpdate = getClassData(clazz, source, fields, sourceTypeCount, sourceClass);
				getFieldData(e, source, fields, infoList, panelList, needsUpdate, sourceWords);
			} catch (Exception e1) {
				logger.error("error when get info of extractor", e1);
				continue;
			}
		}

		// 分析内部类
		List<Class<?>> innerClass = new ArrayList<>();
		List<String> sources = new ArrayList<>();
		
		int len = innerClass.size();

		for (int i = 0; i < len; ++i) {
			Class<?> c = innerClass.get(i);
			String s = sources.get(i);
			Field[] fs = c.getDeclaredFields();
			List<String> sourceWordsList = new ArrayList<>();
			try {
				getClassData(c, s, fs, sourceTypeCount, sourceClass);
				for(Field f: fs) {
					ExtractorField[] attr = f.getAnnotationsByType(ExtractorField.class);
					if (attr == null) {
						continue;
					}

					for (int k = 0; k < attr.length; ++k) {
						if (attr[i].value().equals(FieldTypeEnum.WEB_USE)) {
							sourceWordsList.add(f.getName());
							break;
						}
					}
				}
			} catch (Exception e) {
				logger.error("error when get info of extractor class "+c.getName(), e);
				continue;
			}
			sourceWords.put(s, sourceWordsList);
		}


		res.put("infoList", infoList);
		res.put("panelList", panelList);
		res.put("totalCount", totalCount);
		res.put("sourceTypeCount", sourceTypeCount);
		res.put("sourceWords", sourceWords);
		res.put("sourceClass", sourceClass);

		resp.setCharacterEncoding("UTF-8");
		PrintWriter writer = resp.getWriter();
		writer.append(res.toString());
	}
	
	// 分析各Extractor的类信息
	private boolean getClassData(Class<?> clazz, String source, Field[] fields, Map<String, Integer> sourceTypeCount,
			 Map<String, String> sourceClass)
					throws Exception {
		boolean needsUpdate = false;
		String className = clazz.getName();

		if (sourceTypeCount.containsKey(source)) {
			sourceTypeCount.put(source, sourceTypeCount.get(source)+1);
		} else {
			sourceTypeCount.put(source, 1);
		}
		
		if (!sourceClass.containsKey(source)) {
			sourceClass.put(source, className);
			needsUpdate = true;
		} else if (!sourceClass.get(source).equals(className)) {
			sourceClass.put(source, String.format("%s|%s", sourceClass.get(source), className));
		}
		
		return needsUpdate;
	}
	
	// 分析每个Extractor的信息
	private void getFieldData(Extractor e, String source, Field[] fields,
			List<Object> infoList, List<Object> panelList,
			boolean needsUpdate, Map<String, List<String>> sourceWords) throws Exception {
		Map<String, Object> info = new HashMap<>();
		Map<String, Object> panel = new HashMap<>();
		List<String> sourceWordsList = new ArrayList<>();

		info.put("className", e.getClass().getName());
		info.put("id", e.id.toString());
		for(Field f: fields) {
			ExtractorField[] attr = f.getAnnotationsByType(ExtractorField.class);
			if (attr == null) {
				continue;
			}
			boolean useInWeb = false;
			boolean useInPanel = false;

			for (int i = 0; i < attr.length; ++i) {
				if (attr[i].value().equals(FieldTypeEnum.WEB_USE)) {
					useInWeb = true;
				}
				if (attr[i].value().equals(FieldTypeEnum.SHOW_IN_PANEL)) {
					useInPanel = true;
				}
			}			
			
			f.setAccessible(true);
			String fieldName = f.getName();
			Object fieldValue = f.get(e);
			if (useInWeb) {
				info.put(fieldName, fieldValue);
			}
			if (useInPanel) {
				panel.put(fieldName, fieldValue);
			}
			if (needsUpdate && useInWeb) {
				sourceWordsList.add(fieldName);				
			}
		}

		infoList.add(info);
		panelList.add(panel);
		if (needsUpdate) {
			sourceWords.put(source, sourceWordsList);
		}
	}

	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		logger.info("POST /api/source");
		String [] paths = req.getPathInfo().split("/");
		String action = paths[paths.length-1];

		UUID uuid = null;

		// 处理对于MetaSource的增删改
		String words;
		boolean success = true;
		switch (action) {
		case "add":
			try {
				String className = req.getParameter("label");
				words = req.getParameter("words");
				config.addExtractors(className, urlToMap(words));
			} catch (Exception ex) {
				logger.error("error when add Extractor", ex);
				success = false;
			}
			break;
		case "delete":
			try {
				uuid = UUID.fromString(req.getParameter("label"));
				config.deleteExtractor(uuid);
			} catch (Exception ex) {
				logger.error("error when delete Extractor", ex);
				success = false;
			}
			break;
		case "update":
			try {
				words = req.getParameter("words");
				uuid = UUID.fromString(req.getParameter("label"));
				config.updateExtractor(uuid, urlToMap(words));
			} catch (Exception ex) {
				logger.error("error when update Extractor", ex);
				success = false;
			}
			break;
		case "save":
			try {
				config.saveExtractors();
			} catch (Exception e) {
				success = false;
			}
		default:
			success = false;
			break;
		}
		
		PrintWriter writer = resp.getWriter();
		writer.append(success ? "1" : "0");
	}

	// 将url形式的字符串转化为Map
	private Map<String, String> urlToMap(String url) {
		HashMap<String, String> wordsMap = new HashMap<String, String>();
		String[] fields = url.split("&");
		for (String s: fields) {
			String[] kv = s.split("=");
			if (kv.length > 1) {
				wordsMap.put(kv[0], kv[1]);
			} else {
				wordsMap.put(kv[0], "");
			}
		}
		return wordsMap;
	}
	
}
